MACRO(PROJECT_VERSION MAJOR MINOR PATCH)
	SET(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR})
	SET(CPACK_PACKAGE_VERSION_MINOR ${MINOR})
	SET(CPACK_PACKAGE_VERSION_PATCH ${PATCH})
	INCLUDE(CPack)
	MESSAGE("Building Makefile for ${CMAKE_PROJECT_NAME} ${CPACK_PACKAGE_VERSION}")
	ADD_DEFINITIONS(
		-DPACKAGE_NAME="${CMAKE_PROJECT_NAME}"
		-DPACKAGE_VERSION_STRING="${CPACK_PACKAGE_VERSION}"
		)
	IF(${CMAKE_GENERATOR} STREQUAL "NMake Makefiles")
	ELSE(${CMAKE_GENERATOR} STREQUAL "NMake Makefiles")
		ADD_DEFINITIONS(-DPACKAGE_VERSION_ID=${MAJOR}<<16|${MINOR}<<8|${PATCH})
	ENDIF(${CMAKE_GENERATOR} STREQUAL "NMake Makefiles")
ENDMACRO(PROJECT_VERSION)

macro(append _name value_)
	set(_values ${ARGV})
	list(REMOVE_AT _values 0)
	foreach(_value ${_values})
		if(${_name})
			set(${_name} "${${_name}} ${_value}")
		else(${_name})
			set(${_name} ${_value})
		endif(${_name})
	endforeach(_value)
endmacro(append)

macro(remove _name value_)
	set(_values ${ARGV})
	list(REMOVE_AT _values 0)
	foreach(_value ${_values})
		if(${_name})
			string(REGEX REPLACE " ${_value} " " " ${_name} ${${_name}})
			string(REGEX REPLACE " ${_value}$" "" ${_name} ${${_name}})
			string(REGEX REPLACE "^${_value}( |$)" "" ${_name} ${${_name}})
		endif(${_name})
	endforeach(_value)
endmacro(remove)

macro(enable _name value_)
	set(__values ${ARGV})
	list(REMOVE_AT __values 0)
	remove(${_name} ${__values})
	append(${_name} ${__values})
endmacro(enable)

macro(append_target_property _target _property value_)
	set(_values ${ARGV})
	list(REMOVE_AT _values 0)
	list(REMOVE_AT _values 0)
	get_target_property(_propertyValue ${_target} ${_property})
	enable(_propertyValue ${_values})
	set_target_properties(${_target} PROPERTIES ${_property} "${_propertyValue}")
endmacro(append_target_property)

macro(enable_unicode)
	add_definitions(-D_UNICODE)
	add_definitions(-DUNICODE)
	enable(CMAKE_CREATE_WIN32_EXE   /ENTRY:wWinMainCRTStartup)
endmacro(enable_unicode)

MACRO(ENABLE_WARNING)
	IF(CMAKE_COMPILER_IS_GNUCC)
		enable(CMAKE_CXX_FLAGS -Wall)
		enable(CMAKE_C_FLAGS   -Wall)
	ENDIF(CMAKE_COMPILER_IS_GNUCC)
ENDMACRO(ENABLE_WARNING)

MACRO(ENABLE_WARNING_STRICT)
	IF(CMAKE_COMPILER_IS_GNUCC)
		SET(MY_CFLAGS "-pedantic -Wshadow -Wunused -Wstrict-aliasing")
		enable(CMAKE_CXX_FLAGS ${MY_CFLAGS})
		enable(CMAKE_C_FLAGS   ${MY_CFLAGS} -std=gnu99)
	ELSE(CMAKE_COMPILER_IS_GNUCC)
		remove(CMAKE_CXX_FLAGS /W3)
		remove(CMAKE_C_FLAGS   /W3)
		enable(CMAKE_CXX_FLAGS /W4)
		enable(CMAKE_C_FLAGS   /W4)

		enable(CMAKE_C_FLAGS_DEBUG   /RTCc /Wp64)
		enable(CMAKE_CXX_FLAGS_DEBUG /RTCc /Wp64)
	ENDIF(CMAKE_COMPILER_IS_GNUCC)
ENDMACRO(ENABLE_WARNING_STRICT)

macro(enable_warning_as_error)
	if(CMAKE_COMPILER_IS_GNUCC)
		enable(CMAKE_CXX_FLAGS -Werror)
		enable(CMAKE_C_FLAGS   -Werror)
	else(CMAKE_COMPILER_IS_GNUCC)
		enable(CMAKE_CXX_FLAGS -WX)
		enable(CMAKE_C_FLAGS   -WX)
	endif(CMAKE_COMPILER_IS_GNUCC)
endmacro(enable_warning_as_error)

MACRO(ENABLE_STRICT_ALIASING)
	IF(CMAKE_COMPILER_IS_GNUCC)
		enable(CMAKE_CXX_FLAGS -Wstrict-aliasing -fstrict-aliasing)
		enable(CMAKE_C_FLAGS   -Wstrict-aliasing -fstrict-aliasing)
	ENDIF(CMAKE_COMPILER_IS_GNUCC)
ENDMACRO(ENABLE_STRICT_ALIASING)

MACRO(CHECK_FLAGS _result _var _type _flags)
	FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/conftest.${_type} "
	     int main()
	     {
	         return 0;
	     }
	     ")
	TRY_COMPILE(${_result}
			${CMAKE_CURRENT_BINARY_DIR}
			${CMAKE_CURRENT_BINARY_DIR}/conftest.${_type}
			CMAKE_FLAGS ${_flags}
			OUTPUT_VARIABLE
			OUTPUT)
ENDMACRO(CHECK_FLAGS)

MACRO(TRY_ENABLE_FLAGS _var _type _flags)
	CHECK_FLAGS(_enabled ${var} ${_type} ${_flags})
	IF(_enabled)
		enable(${_var} ${_flags})
	ENDIF(_enabled)
ENDMACRO(TRY_ENABLE_FLAGS)

MACRO(ENABLE_VISIBILITY_HIDDEN)
	IF(NOT DEFINED HAVE_VISIBILITY)
		CHECK_FLAGS(HAVE_VISIBILITY CMAKE_CXX_FLAGS cpp -fvisibility=hidden)
	ENDIF(NOT DEFINED HAVE_VISIBILITY)
	IF(HAVE_VISIBILITY)
		enable(CMAKE_CXX_FLAGS -fvisibility=hidden)
		ADD_DEFINITIONS(-DHAVE_VISIBILITY)
	ENDIF(HAVE_VISIBILITY)
ENDMACRO(ENABLE_VISIBILITY_HIDDEN)


MACRO(QUICK_CONFIGURE_FILE filename)
	CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${filename}.cmake ${CMAKE_CURRENT_BINARY_DIR}/${filename})
	INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
ENDMACRO(QUICK_CONFIGURE_FILE)

macro(msvc_set_runtime _type)
	if(MSVC)
		if(${_type} STREQUAL STATIC)
			set(_flag T)
		else(${_type} STREQUAL STATIC)
			set(_flag D)
		endif(${_type} STREQUAL STATIC)
		enable(CMAKE_C_FLAGS_RELEASE          /M${_flag})
		enable(CMAKE_CXX_FLAGS_RELEASE        /M${_flag})
		enable(CMAKE_C_FLAGS_RELWITHDEBINFO   /M${_flag})
		enable(CMAKE_CXX_FLAGS_RELWITHDEBINFO /M${_flag})
		enable(CMAKE_C_FLAGS_DEBUG            /M${_flag}d)
		enable(CMAKE_CXX_FLAGS_DEBUG          /M${_flag}d)
	endif(MSVC)
endmacro(msvc_set_runtime)

macro(add_resource_library _target _type _rcfile)
	add_library(${_target} ${_type} ${_rcfile})
	if(${_type} STREQUAL SHARED)
		if(CMAKE_COMPILER_IS_GNUCC)
			# TODO
		else(CMAKE_COMPILER_IS_GNUCC)
			append_target_property(${_target} LINK_FLAGS /NOENTRY)
		endif(CMAKE_COMPILER_IS_GNUCC)
	endif(${_type} STREQUAL SHARED)
endmacro(add_resource_library)

macro(enable_string_pooling)
	if(MSVC)
		enable(CMAKE_C_FLAGS   /GF)
		enable(CMAKE_CXX_FLAGS /GF)
	endif(MSVC)
endmacro(enable_string_pooling)

macro(enable_set_se_translator)
	if(MSVC)
		remove(CMAKE_C_FLAGS   /EHsc)
		remove(CMAKE_CXX_FLAGS /EHsc)
		enable(CMAKE_C_FLAGS   /EHa)
		enable(CMAKE_CXX_FLAGS /EHa)
	endif(MSVC)
endmacro(enable_set_se_translator)

macro(enable_devel_features)
	if(MSVC)
		# edit & continue
		remove(CMAKE_C_FLAGS_DEBUG   /Zi)
		remove(CMAKE_CXX_FLAGS_DEBUG /Zi)
		enable(CMAKE_C_FLAGS_DEBUG   /ZI)
		enable(CMAKE_CXX_FLAGS_DEBUG /ZI)

		# minimal rebuild
		enable(CMAKE_C_FLAGS_DEBUG   /Gm)
		enable(CMAKE_CXX_FLAGS_DEBUG /Gm)
	endif(MSVC)
	enable_warning()
endmacro(enable_devel_features)

macro(enable_release_features)
	if(MSVC)
		enable(CMAKE_C_FLAGS_RELEASE          /GL /Oy)
		enable(CMAKE_CXX_FLAGS_RELEASE        /GL /Oy)
		enable(CMAKE_C_FLAGS_MINSIZEREL       /GL /Oy)
		enable(CMAKE_CXX_FLAGS_MINSIZEREL     /GL /Oy)
		enable(CMAKE_C_FLAGS_RELWITHDEBINFO   /GL)
		enable(CMAKE_CXX_FLAGS_RELWITHDEBINFO /GL)

		remove(CMAKE_EXE_LINKER_FLAGS_RELEASE          /INCREMENTAL:YES /INCREMENTAL:NO)
		remove(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL       /INCREMENTAL:YES /INCREMENTAL:NO)
		remove(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO   /INCREMENTAL:YES /INCREMENTAL:NO)

		remove(CMAKE_SHARED_LINKER_FLAGS_RELEASE          /INCREMENTAL:YES /INCREMENTAL:NO)
		remove(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL       /INCREMENTAL:YES /INCREMENTAL:NO)
		remove(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO   /INCREMENTAL:YES /INCREMENTAL:NO)

		remove(CMAKE_MODULE_LINKER_FLAGS_RELEASE          /INCREMENTAL:YES /INCREMENTAL:NO)
		remove(CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL       /INCREMENTAL:YES /INCREMENTAL:NO)
		remove(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO   /INCREMENTAL:YES /INCREMENTAL:NO)

		enable(CMAKE_EXE_LINKER_FLAGS_RELEASE           /LTCG)
		enable(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL        /LTCG)
		enable(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO    /LTCG /OPT:REF /OPT:ICF)
		enable(CMAKE_SHARED_LINKER_FLAGS_RELEASE        /LTCG)
		enable(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL     /LTCG)
		enable(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO /LTCG /OPT:REF /OPT:ICF)
		enable(CMAKE_MODULE_LINKER_FLAGS_RELEASE        /LTCG)
		enable(CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL     /LTCG)
		enable(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO /LTCG /OPT:REF /OPT:ICF)
	endif(MSVC)
endmacro(enable_release_features)

macro(msvc_disable_warning _warning)
	if(MSVC)
		enable(CMAKE_C_FLAGS   /wd${_warning})
		enable(CMAKE_CXX_FLAGS /wd${_warning})
	endif(MSVC)
endmacro(msvc_disable_warning)

macro(msvc_disable_warning_charset)
	msvc_disable_warning(4819)
endmacro(msvc_disable_warning_charset)

macro(disable_warning_unused)
	msvc_disable_warning(4100)
endmacro(disable_warning_unused)

macro(init_target_output_name_ex _pattern target_)
	set(_targets ${ARGV})
	list(REMOVE_AT _targets 0)

	foreach(_target ${_targets})
		set(_outputname_d ${_pattern})
		string(REPLACE "%1%" "${_target}" _outputname_d ${_outputname_d})
		string(REPLACE "%2%" "D" _outputname_d ${_outputname_d})

		set(_outputname ${_pattern})
		string(REPLACE "%1%" "${_target}" _outputname ${_outputname})
		string(REPLACE "%2%" "" _outputname ${_outputname})

		set_target_properties(
			${_targets}
			PROPERTIES
			OUTPUT_NAME_DEBUG          ${_outputname_d}
			OUTPUT_NAME_RELEASE        ${_outputname}
			OUTPUT_NAME_RELWITHDEBINFO ${_outputname}
			OUTPUT_NAME_MINSIZEREL     ${_outputname}
		)
	endforeach(_target)
endmacro(init_target_output_name_ex)

macro(init_target_output_name _target)
	init_target_output_name_ex("%1%%2%" ${_targets})
endmacro(init_target_output_name)

macro(_pch_prepare_sources _header sources_)
	set(__sources ${ARGV})
	list(REMOVE_AT __sources 0)

	if(CMAKE_COMPILER_IS_GNUCC)
		set(_add_prop "-include ${_header}")
	else(CMAKE_COMPILER_IS_GNUCC)
		set(_add_prop "/FI${_header}")
	endif(CMAKE_COMPILER_IS_GNUCC)

	foreach(_source ${_sources})
		get_source_file_property(_prop ${_source} COMPILE_FLAGS)
		if(_prop)
			set(_prop "${prop} ${_add_prop}")
		else(_prop)
			set(_prop "${_add_prop}")
		endif(_prop)
		set_source_files_properties(${sources} PROPERTIES COMPILE_FLAGS ${_prop})
	endforeach(_source ${_sources})
endmacro(_pch_prepare_sources)

# include PCH first to use these functions
macro(pch_add_library _header _target sources_)
	set(_sources ${ARGV})
	list(REMOVE_AT _sources 0)
	list(REMOVE_AT _sources 0)

	set(_args)
	list(GET _sources 0 _arg)
	while(_arg MATCHES "^(STATIC|SHARED|MODULE|EXCLUDE_FROM_ALL)$")
		list(APPEND _args ${_arg})
		list(REMOVE_AT _sources 0)
		list(GET _sources 0 _arg)
	endwhile(_arg MATCHES "^(STATIC|SHARED|MODULE|EXCLUDE_FROM_ALL)$")
	_pch_prepare_sources(${_header} ${_sources})

	get_native_precompiled_header(${_target} ${_header})
	add_library(${_target} ${_args} ${${_target}_pch} ${_sources})
	add_native_precompiled_header(${_target} ${_header})
endmacro(pch_add_library)

macro(pch_add_executable _header _target sources_)
	set(_sources ${ARGV})
	list(REMOVE_AT _sources 0)
	list(REMOVE_AT _sources 0)

	set(_args)
	list(GET _sources 0 _arg)
	while(_arg MATCHES "^(WIN32|MACOSX_BUNDLE|EXCLUDE_FROM_ALL)$")
		list(APPEND _args ${_arg})
		list(REMOVE_AT _sources 0)
		list(GET _sources 0 _arg)
	endwhile(_arg MATCHES "^(WIN32|MACOSX_BUNDLE|EXCLUDE_FROM_ALL)$")
	_pch_prepare_sources(${_header} ${_sources})

	get_native_precompiled_header(${_target} ${_header})
	add_executable(${_target} ${_args} ${${_target}_pch} ${_sources})
	add_native_precompiled_header(${_target} ${_header})
endmacro(pch_add_executable)

macro(sadd_subdirectory _dir)
	if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}/CMakeLists.txt")
		add_subdirectory("${_dir}")
	endif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}/CMakeLists.txt")
endmacro(sadd_subdirectory)

# main
IF(CMAKE_COMPILER_IS_GNUCC)
	enable(CMAKE_C_FLAGS_RELEASE   -O2)
	enable(CMAKE_CXX_FLAGS_RELEASE -O2)

	enable(CMAKE_C_FLAGS_RELWITHDEBINFO   -O2)
	enable(CMAKE_CXX_FLAGS_RELWITHDEBINFO -O2)

	enable(CMAKE_C_FLAGS_DEBUG     -O0 -g -ggdb)
	enable(CMAKE_CXX_FLAGS_DEBUG   -O0 -g -ggdb)
ENDIF(CMAKE_COMPILER_IS_GNUCC)

enable(COMPILE_FLAGS_DEBUG          -DDEBUG)
enable(COMPILE_FLAGS_RELEASE        -DNDEBUG)
enable(COMPILE_FLAGS_RELWITHDEBINFO -DNDEBUG)
enable(COMPILE_FLAGS_MINSIZEREL     -DNDEBUG)

SET(WIN_NATIVE)
IF(MINGW)
	SET(WIN_NATIVE 1)
ENDIF(MINGW)

IF(WIN32 AND NOT CYGWIN)
	SET(WIN_NATIVE 1)
ENDIF(WIN32 AND NOT CYGWIN)

IF(CMAKE_COMPILER_IS_GNUCC)
	ADD_DEFINITIONS("-D__ATTRIBUTE_UNUSED__=__attribute__((unused))")
ELSE(CMAKE_COMPILER_IS_GNUCC)
	ADD_DEFINITIONS("-D__ATTRIBUTE_UNUSED__")
ENDIF(CMAKE_COMPILER_IS_GNUCC)

if(NOT CMAKE_RC_CREATE_SHARED_LIBRARY)
	if(CMAKE_C_CREATE_SHARED_LIBRARY)
		set(CMAKE_RC_CREATE_SHARED_LIBRARY ${CMAKE_C_CREATE_SHARED_LIBRARY})
	else(CMAKE_C_CREATE_SHARED_LIBRARY)
		if(CMAKE_CC_CREATE_SHARED_LIBRARY)
			set(CMAKE_RC_CREATE_SHARED_LIBRARY ${CMAKE_CC_CREATE_SHARED_LIBRARY})
		endif(CMAKE_CC_CREATE_SHARED_LIBRARY)
	endif(CMAKE_C_CREATE_SHARED_LIBRARY)
endif(NOT CMAKE_RC_CREATE_SHARED_LIBRARY)

if(NOT CMAKE_RC_CREATE_STATIC_LIBRARY)
	if(CMAKE_C_CREATE_STATIC_LIBRARY)
		set(CMAKE_RC_CREATE_STATIC_LIBRARY ${CMAKE_C_CREATE_STATIC_LIBRARY})
	else(CMAKE_C_CREATE_STATIC_LIBRARY)
		if(CMAKE_CC_CREATE_STATIC_LIBRARY)
			set(CMAKE_RC_CREATE_STATIC_LIBRARY ${CMAKE_CC_CREATE_STATIC_LIBRARY})
		endif(CMAKE_CC_CREATE_STATIC_LIBRARY)
	endif(CMAKE_C_CREATE_STATIC_LIBRARY)
endif(NOT CMAKE_RC_CREATE_STATIC_LIBRARY)
